package com.anti.generate.modular.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.anti.commom.core.exception.BusinessException;
import com.anti.generate.core.config.Config;
import com.anti.generate.core.context.XnVelocityContext;
import com.anti.generate.core.param.XnCodeGenParam;
import com.anti.generate.core.tool.StringDateTool;
import com.anti.generate.core.util.Util;
import com.anti.generate.modular.entity.CodeGenerate;
import com.anti.generate.modular.entity.SysCodeGenerateConfig;
import com.anti.generate.modular.enums.CodeGenerateExceptionEnum;
import com.anti.generate.modular.param.CodeGenerateParam;
import com.anti.generate.modular.param.SysCodeGenerateConfigParam;
import com.anti.generate.modular.result.InforMationColumnsResult;
import com.anti.generate.modular.result.InformationResult;
import com.anti.generate.modular.service.CodeGenerateService;
import com.anti.generate.modular.service.SysCodeGenerateConfigService;
import com.anti.jdbc.DBUtils;
import com.anti.jdbc.support.page.PageParam;
import com.anti.jdbc.support.page.PageResult;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description TODO
 * @Author anTi
 * @Date 2021-3-22
 */
@Service
public class CodeGenerateServiceImpl implements CodeGenerateService {
    /**
     * 模板后缀
     */
    private static String TEMP_SUFFIX = ".vm";

    /**
     * 转换的编码
     */
    private static String ENCODED = "UTF-8";

    /**
     * 转换模板名称所需变量
     */
    private static String ADD_FORM_PAGE_NAME = "addForm.vue";
    private static String EDIT_FORM_PAGE_NAME = "editForm.vue";
    private static String INDEX_PAGE_NAME = "index.vue";
    private static String MANAGE_JS_NAME = "Manage.js";
    private static String SQL_NAME = ".sql";
    private static String JAVA_SUFFIX = ".java";
    private static String TEMP_ENTITY_NAME = "entity";
    private static String dbName = "jpzlxt";
    @Resource
    DBUtils dbUtils;
    @Resource
    SysCodeGenerateConfigService sysCodeGenerateConfigService;

    @Override
    public PageResult page(PageParam pageParam, CodeGenerateParam codeGenerateParam) {
        StringBuilder sb=new StringBuilder("select * from sys_code_generate where 1=1 ");
        ArrayList<Object> param = CollectionUtil.newArrayList();
        if (ObjectUtil.isNotNull(codeGenerateParam)) {
            //根据表名模糊查询
            if (ObjectUtil.isNotEmpty(codeGenerateParam.getTableName())) {
                sb.append(" and table_name like ?");
                param.add("%"+codeGenerateParam.getTableName()+"%");
            }
        }
        return dbUtils.query(pageParam,sb.toString(),param.toArray());
    }

    @Override
    public void add(CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = new CodeGenerate();
        BeanUtil.copyProperties(codeGenerateParam, codeGenerate);
        if (!vldTablePri(codeGenerate.getTableName())) {
            throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_TABLE_NOT_PRI);
        }
        String id = dbUtils.insertKey(codeGenerate).toString();
        codeGenerate.setId(Long.parseLong(id));
        // 加入配置表中
        codeGenerateParam.setId(codeGenerate.getId());
        this.sysCodeGenerateConfigService.addList(this.getInforMationColumnsResultList(codeGenerateParam), codeGenerate);
    }

    @Override
    public void delete(List<CodeGenerateParam> codeGenerateParamList) {
        String sql="delete from sys_code_generate where id =?";
        codeGenerateParamList.forEach(codeGenerateParam -> {
            dbUtils.execSql(sql,codeGenerateParam.getId());
            SysCodeGenerateConfigParam sysCodeGenerateConfigParam = new SysCodeGenerateConfigParam();
            sysCodeGenerateConfigParam.setCodeGenId(codeGenerateParam.getId());
            this.sysCodeGenerateConfigService.delete(sysCodeGenerateConfigParam);
        });
    }

    @Override
    public void edit(CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = this.queryCodeGenerate(codeGenerateParam);
        BeanUtil.copyProperties(codeGenerateParam, codeGenerate);
        if (!vldTablePri(codeGenerate.getTableName())) {
            throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_TABLE_NOT_PRI);
        }
        dbUtils.update(codeGenerate);
    }

    @Override
    public CodeGenerate detail(CodeGenerateParam codeGenerateParam) {
        return this.queryCodeGenerate(codeGenerateParam);
    }

    @Override
    public List<InformationResult> InformationTableList() {
        String sql=" select table_name,create_time,update_time,table_comment from information_schema.tables where table_schema = ?";

        return dbUtils.query(sql,InformationResult.class,dbName);
    }

    @Override
    public void runLocal(CodeGenerateParam codeGenerateParam) {
        XnCodeGenParam xnCodeGenParam = copyParams(codeGenerateParam);
        codeGenLocal(xnCodeGenParam);
    }

    @Override
    public void runDown(CodeGenerateParam codeGenerateParam, HttpServletResponse response) {
        XnCodeGenParam xnCodeGenParam = copyParams(codeGenerateParam);
        downloadCode(xnCodeGenParam, response);
    }
    /**
     * 下载方式组装代码基础
     */
    private void downloadCode(XnCodeGenParam xnCodeGenParam, HttpServletResponse response) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        codeGenDown(xnCodeGenParam, zipOutputStream);
        IOUtils.closeQuietly(zipOutputStream);
        outputStream.toByteArray();
        try {
            Util.DownloadGen(response, outputStream.toByteArray());
        } catch (Exception e) {
            throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_NOT_PATH);
        }
    }

    /**
     * 下载ZIP方式
     */
    private void codeGenDown (XnCodeGenParam xnCodeGenParam,ZipOutputStream zipOutputStream) {
        Util.initVelocity();
        XnVelocityContext context = new XnVelocityContext();
        VelocityContext velContext = context.createVelContext(xnCodeGenParam);
        String[] filePath = Config.xnCodeGenFilePath(xnCodeGenParam.getBusName(), xnCodeGenParam.getPackageName());
        for (int a = 0; a < filePath.length; a++) {
            String templateName = Config.xnCodeGenTempFile[a];
            String fileBaseName = ResetFileBaseName(xnCodeGenParam.getClassName(),
                    templateName.substring(templateName.indexOf(Config.FILE_SEP) + 1,
                            templateName.lastIndexOf(TEMP_SUFFIX)));
            XnZipOutputStream(velContext,Config.templatePath + templateName,filePath[a] + fileBaseName,zipOutputStream);
        }
    }
    /**
     * 生成ZIP
     */
    private void XnZipOutputStream (VelocityContext velContext, String tempName, String fileBaseName, ZipOutputStream zipOutputStream) {
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate(tempName, ENCODED);
        tpl.merge(velContext, sw);
        try {
            // 添加到zip
            zipOutputStream.putNextEntry(new ZipEntry(fileBaseName));
            IOUtils.write(sw.toString(), zipOutputStream, ENCODED);
            IOUtils.closeQuietly(sw);
            zipOutputStream.flush();
            zipOutputStream.closeEntry();
        } catch (IOException e) {
            throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_NOT_PATH);
        }
    }
    /**
     * 本地项目生成
     */
    private void codeGenLocal (XnCodeGenParam xnCodeGenParam) {
        XnVelocityContext context = new XnVelocityContext();
        //初始化参数
        Properties properties=new Properties();
        //设置velocity资源加载方式为class
        properties.setProperty("resource.loader", "class");
        //设置velocity资源加载方式为file时的处理类
        properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        //实例化一个VelocityEngine对象
        VelocityEngine velocityEngine=new VelocityEngine(properties);

        String[] filePath = Config.xnCodeGenFilePath(xnCodeGenParam.getBusName(), xnCodeGenParam.getPackageName());
        for (int i = 0; i < filePath.length; i++) {
            String templateName = Config.xnCodeGenTempFile[i];

            String fileBaseName = ResetFileBaseName(xnCodeGenParam.getClassName(),
                    templateName.substring(templateName.indexOf(Config.FILE_SEP) + 1, templateName.lastIndexOf(TEMP_SUFFIX)));
            String path = Config.getLocalPath ();
            // 前端VUE位置有所变化, sql同样根目录
            if (fileBaseName.contains(INDEX_PAGE_NAME) || fileBaseName.contains(ADD_FORM_PAGE_NAME) ||
                    fileBaseName.contains(EDIT_FORM_PAGE_NAME) ||fileBaseName.contains(MANAGE_JS_NAME) ||
                    fileBaseName.contains(SQL_NAME)) {
                path = Config.getLocalFrontPath();
            }

            File file = new File(path + filePath[i] + fileBaseName);

            //判断是否覆盖存在的文件
            if(file.exists() && !Config.FLAG){
                continue;
            }

            //获取父目录
            File parentFile = file.getParentFile();
            if(!parentFile.exists()){
                parentFile.mkdirs();
            }
            try {
                Writer writer = new FileWriter(file);
                velocityEngine.mergeTemplate(Config.templatePath + templateName,ENCODED,context.createVelContext(xnCodeGenParam),writer);
                writer.close();
            } catch (Exception e) {
                throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_NOT_PATH);
            }
        }
    }


    /**
     * 重置文件名称
     */
    private static String ResetFileBaseName (String className,String fileName) {
        String fileBaseName = className + fileName;
        // 实体类名称单独处理
        if (fileBaseName.contains(TEMP_ENTITY_NAME)) {
            return className + JAVA_SUFFIX;
        }
        // 首页index.vue界面
        if (fileBaseName.contains(INDEX_PAGE_NAME)) {
            return INDEX_PAGE_NAME;
        }
        // 表单界面名称
        if (fileBaseName.contains(ADD_FORM_PAGE_NAME)) {
            return ADD_FORM_PAGE_NAME;
        }
        if (fileBaseName.contains(EDIT_FORM_PAGE_NAME)) {
            return EDIT_FORM_PAGE_NAME;
        }
        // js名称
        if (fileBaseName.contains(MANAGE_JS_NAME)) {
            return className.substring(0,1).toLowerCase() + className.substring(1) + MANAGE_JS_NAME;
        }
        return fileBaseName;
    }


    private XnCodeGenParam copyParams (CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = this.queryCodeGenerate(codeGenerateParam);
        SysCodeGenerateConfigParam sysCodeGenerateConfigParam = new SysCodeGenerateConfigParam();
        sysCodeGenerateConfigParam.setCodeGenId(codeGenerateParam.getId());
        List<SysCodeGenerateConfig> configList = this.sysCodeGenerateConfigService.list(sysCodeGenerateConfigParam);
        XnCodeGenParam param = new XnCodeGenParam();
        BeanUtil.copyProperties(codeGenerate, param);
        // 功能名
        param.setFunctionName(codeGenerate.getTableComment());
        param.setConfigList(configList);
        param.setCreateTimeString(StringDateTool.getStringDate());
        return param;
    }
    /**
     * 获取表中所有字段集合
     */
    private List<InforMationColumnsResult> getInforMationColumnsResultList (CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = this.queryCodeGenerate(codeGenerateParam);
        String sql="  select  column_name,data_type,column_comment,column_key   from information_schema.columns   where   table_schema =?  and table_name =?";
        List<InforMationColumnsResult> inforMationColumnsResultList = dbUtils.query(sql,InforMationColumnsResult.class,dbName, codeGenerate.getTableName());
        return  inforMationColumnsResultList;
    }

    /**
     * 获取代码生成基础配置
     */
    private CodeGenerate queryCodeGenerate(CodeGenerateParam codeGenerateParam) {
        CodeGenerate codeGenerate = dbUtils.get("select * from sys_code_generate where id =?",CodeGenerate.class,codeGenerateParam.getId());
        if (ObjectUtil.isNull(codeGenerate)) {
            throw new BusinessException(CodeGenerateExceptionEnum.CODE_GEN_NOT_EXIST);
        }
        return codeGenerate;
    }
    /**
     * 校验表中是否包含主键
     */
    private boolean vldTablePri (String tableName) {
        String sql="select  column_name,data_type,column_comment,column_key   from information_schema.columns   where   table_schema =?  and table_name =?";
        List<InforMationColumnsResult> inforMationColumnsResultList = dbUtils.query(sql,InforMationColumnsResult.class,dbName, tableName);
        for (int a = 0; a < inforMationColumnsResultList.size(); a++) {
            if (inforMationColumnsResultList.get(a).columnKey.equals(Config.DB_TABLE_COM_KRY)) {
                return true;
            }
        }
        return false;
    }
}
